home *** CD-ROM | disk | FTP | other *** search
/ Aminet 32 / Aminet 32 (1999)(Schatztruhe)[!][Aug 1999].iso / Aminet / docs / help / HowToWriteEmu.lha / HOWTO.TEXT
Encoding:
Text File  |  1998-12-10  |  16.9 KB  |  403 lines

  1. HOWTO: Writing a computer emulator
  2.  
  3. How To Write a Computer Emulator
  4.  
  5. by Marat Fayzullin
  6.  
  7.    I wrote this document after receiving large amount of email from people who
  8. would like to write an emulator of one or another computer, but do not know
  9. where to start. Any opinions and advices contained in the following text are
  10. mine alone and should not be taken for an absolute truth. The document mainly
  11. covers so-called "interpreting" emulators, as opposed to "compiling" ones,
  12. because I do not have much experience with recompilation techniques. It does
  13. have a pointer or two to the places where you can find information on these
  14. techniques.
  15.  
  16.   If you feel that this document is missing something or want to make a
  17. correction, feel free to email me your comments. I do not answer to flames,
  18. idiocy, and requests for ROM images though. I'm badly missing some important
  19. FTP/WWW addresses in the end of this document, so if you know any worth putting
  20. there, tell me about it. Same goes for any frequently asked questions you may
  21. have, that are not in this document.
  22.  
  23. Contents
  24.  
  25.   So, you decided to write a software emulator? Very well, then this document
  26. may be of some help to you. It covers some common technical questions people ask
  27. about writing emulators.
  28.  
  29.  * What can be emulated?
  30.  * What is "emulation" and how does it differ from "simulation"?
  31.  * Is it legal to emulate the proprietary hardware?
  32.  * What is "interpreting emulator" and how does it differ from "recompiling
  33. emulator"?
  34.  * I want to write an emulator. Where should I start?
  35.  * Which programming language should I use?
  36.  * Where do I get information on the emulated hardware?
  37.  * How do I emulate a CPU?
  38.  * How do I optimize C code?
  39.  * More to come here
  40.  
  41. What can be emulated?
  42.  
  43.   Basically, anything which has a microprocessor inside. Of course, only devices
  44. running a more or less flexible program are interesting to emulate. Those
  45. include:
  46.  
  47.  * Computers
  48.  * Calculators
  49.  * Videogame Consoles
  50.  * Arcade Videogames
  51.  * etc.
  52.  
  53.   It is necessary to note that you can emulate any computer system, even if it
  54. is very complex (such as Commodore Amiga computer, for example). The perfomance
  55. of such an emulation may be very low though.
  56.  
  57. What is "emulation" and how does it differ from "simulation"?
  58.  
  59.   Emulation is an attempt to imitate the internal design of a device.Simulation
  60. is an attempt to imitate functions of a device. For example, a program imitating
  61. the Pacman arcade hardware and running real Pacman ROM on it is an emulator. A
  62. Pacman game written for your computer but using graphics similar to a real
  63. arcade is a simulator.
  64.  
  65. Is it legal to emulate the proprietary hardware?
  66.  
  67.   Although the matter lies in the "gray" area, it appears to be legal to emulate
  68. proprietary hardware, as long as the information on it hasn't been obtained by
  69. illegal means. You should also be aware of the fact that it is illegal to
  70. distribute the system ROMs (BIOS, etc.) with the emulator if the are
  71. copyrighted.
  72.  
  73. What is "interpreting emulator" and how does it differ from "recompiling
  74. emulator"?
  75.  
  76.   There are three basic schemes which can be used for an emulator. They can be
  77. combined for the best result.
  78.  
  79.  * Interpretation
  80.   The emulator reads emulated code from the memory byte-by-byte, decodes it, and
  81. performs the appropriate commands on the emulated registers, memory, and I/O.
  82. The general algorithm of such an emulator is following:
  83. while(CPUIsRunning)
  84. {
  85.   Fetch OpCode
  86.   Interpret OpCode
  87. }
  88.   The pluses of such code include ease of debugging, portability, and ease of
  89. synchronization (you can simply count the clock cycles passed and tie the rest
  90. of your emulation to the cycle count).
  91.  
  92.   The single, big, and obvious minus is perfomance. The interpretation takes a
  93. lot of CPU time, and you may require pretty fast computer to run your code at
  94. the decent speed.
  95.  
  96.  * Static Recompilation
  97.   In this technique, you take a program written in the emulated code and attempt
  98. to translate it into the assembly code of your computer.The result will be usual
  99. executable file which you can run on your computer without any special tools.
  100. While static recompilation sounds very nice, it is not always possible. For
  101. example, you can not statically recompile the self-modifying code, as there is
  102. no way to tell what it will become without running it. To avoid such situations,
  103. you may try combining static recompiler with an interpreter or a dynamic
  104. recompiler.
  105.  
  106.  * Dynamic Recompilation
  107.   Dynamic recompilation is essentially the same thing as the static one, but it
  108. occurs during program execution. Instead of trying to recompile all the code at
  109. once, do it on the fly when you encounter CALL or JUMP instructions. To increase
  110. speed, this technique can be combined with the static recompilation. You can
  111. read more on dynamic recompilation in the white paper by Ardi, creators of the
  112. recompiling Macintosh emulator.
  113.  
  114. I want to write an emulator. Where should I start?
  115.  
  116.   In order to write an emulator, you must have a good general knowledge of
  117. computer programming and digital electronics. Experience in assembly programming
  118. comes very handy too.
  119.  
  120.  *     Select a programming language to use.
  121.  *     Find all available information about the emulated hardware.
  122.  *     Write CPU emulation or get existing code for the CPU emulation.
  123.  * Write some draft code to emulate the rest of the hardware, at    least
  124. partially.
  125.  * At this point, it is useful to write a little built-in debugger    which
  126. allows to stop emulation and see what the program is doing.    You may also need
  127. a disassembler of the emulated system assembly    language. Write your own if
  128. none exist.
  129.  * Try running programs on your emulator.
  130.  * Use disassembler and debugger to see how programs use the hardware    and
  131. adjust your code appropriately.
  132.  
  133. Which programming language should I use?
  134.  
  135.   The most obvious alternatives are C and Assembly. Here are pros and cons of
  136. each of them:
  137.  
  138.  * Assembly Languages
  139. + Generally, allow to produce faster code.
  140. + The emulating CPU registers can be used to directly
  141.   store the registers of the emulated CPU.
  142. + Many opcodes can be emulated with the similar
  143.   opcodes of the emulating CPU.
  144. - The code is not portable, i.e. it can not be run on
  145.   a computer with different architecture.
  146. - It is difficult to debug and maintain the code.
  147.  
  148.  * C
  149. + The code can be made portable so that it works on
  150.   different computers and operating systems.
  151. + It is relatively easy to debug and maintain the
  152.   code.
  153. + Different hypothesis of how real hardware works
  154.   can be tested quickly.
  155. - C is generally slower than pure assembly code.
  156.  
  157.   Good knowledge of the chosen language is an absolute necessity for writing a
  158. working emulator, as it is quite complex project, and your code should be
  159. optimized to run as fast as possible. Computer emulation is definitely not one
  160. of the projects on which you learn a programming language.
  161.  
  162. Where do I get information on the emulated hardware?
  163.  
  164.   Following is a list of places where you may want to look.
  165.  
  166. Newsgroups
  167.  
  168.  * comp.emulators.misc
  169.   This is a newsgroup for the general discussion about computer emulation.Many
  170. emulator authors read it, although the noise level is somewhat high.Read the
  171. c.e.m FAQbefore posting to this newsgroup.
  172.  * comp.emulators.game-consoles
  173.   Same as comp.emulators.misc, but specifically dealing with videogame console
  174. emulators. Read the c.e.m FAQbefore posting to this newsgroup.
  175.  * comp.sys./emulated-system/
  176.   The comp.sys.* hierarchy contains newsgroups dedicated to specific computers.
  177. You may obtain a lot of useful technical information by reading these
  178. newsgroups. Typical examples:
  179. comp.sys.msx       MSX/MSX2/MSX2+/TurboR computers
  180. comp.sys.sinclair  Sinclair ZX80/ZX81/ZXSpectrum/QL
  181. comp.sys.apple2    Apple ][
  182. etc.
  183.   Please, check the appropriate FAQs before posting to these newsgroups.
  184.  * alt.folklore.computers
  185.  
  186.  * rec.games.video.classic
  187.  
  188. FTP
  189.  
  190. Console and Game Programming  site in Oulu, Finland
  191. Arcade Videogame Hardware  archive at ftp.spies.com
  192. Computer History and Emulation  archive at KOMKON
  193.  
  194. WWW
  195.  
  196. comp.emulators.misc FAQ
  197. My Homepage
  198. Arcade Emulation Programming Repository
  199. Emulation Programmer's Resource
  200.  
  201. How do I emulate a CPU?
  202.  
  203. First of all, if you only need to emulate a standard Z80 or 6502 CPU, you can
  204. use one of the CPU emulators I wrote.Certain conditions apply to their usage
  205. though.
  206.  
  207.  For those who want to write their own CPU emulation core or interested to know
  208. how it works, I provide a skeleton of a typical CPU emulator in C below. In the
  209. real emulator, you may want to skip some parts of it and add some others on your
  210. own.
  211. Counter=InterruptPeriod;
  212. PC=InitialPC;
  213.  
  214. for(;;)
  215. {
  216.   OpCode=Memory[PC++];
  217.   Counter-=Cycles[OpCode];
  218.  
  219.   switch(OpCode)
  220.   {
  221.     case OpCode1:
  222.     case OpCode2:
  223.     ...
  224.   }
  225.  
  226.   if(Counter
  227.  
  228. First, we assign initial values to the CPU cycle counter (Counter), and the
  229. program counter (PC):
  230. Counter=InterruptPeriod;
  231. PC=InitialPC;
  232. The Counter contains the number of CPU cycles left to the next suspected
  233. interrupt. Note that interrupt should not necessarily occur when this counter
  234. expires: you can use it for many other purposes, such as synchronizing timers,
  235. or updating scanlines on the screen. More on this later. The PC contains the
  236. memory address from which our emulated CPU will read its next opcode.
  237.  
  238.  After initial values are assigned, we start the main loop:
  239. for(;;)
  240. {
  241. Note that this loop can also be implemented as
  242. while(CPUIsRunning)
  243. {
  244. where CPUIsRunning is a boolean variable. This has certain advantages, as you
  245. can terminate the loop at any moment by setting CPUIsRunning=0. Unfortunately,
  246. checking this variable on every pass takes quite a lot of CPU time, and should
  247. be avoided if possible. Also, do not implement this loop as
  248. while(1)
  249. {
  250. because in this case, some compilers will generate code checking whether 1 is
  251. true or not. You certainly don't want the compiler to do this unnecessary work
  252. on every pass of a loop.
  253.  
  254.  Now, when we are in the loop, the first thing is to read the next opcode, and
  255. modify the program counter: OpCode=Memory[PC++];While this is the simplest and
  256. fastest way to read from the emulated memory, it is not always possible for
  257. following reasons:
  258.  
  259.  * Memory may be fragmented into switchable pages (aka banks)
  260.  * There may be memory-mapped I/O devices in the system
  261.  
  262. In these cases, we can read the emulated memory via ReadMemory() function:
  263. OpCode=ReadMemory(PC++);There should also be a WriteMemory() function to write
  264. into emulated memory. Besides handling memory-mapped I/O and pages,
  265. WriteMemory() should also do the following:
  266.  
  267.  * Protect ROM from writing
  268. Some cartridge-based software (such as MSX games, for example) tries to write
  269. into their own ROM and refuses to work if writing succeeds. This is often done
  270. for copy protection.
  271.  * Handle mirrored memory
  272. An area of memory may be accessible at several different addresses. For example,
  273. the data you write into location $4000 will also appear at $6000 and $8000.
  274. While this situation can be handled in the ReadMemory(), it is usually not
  275. desirable, as ReadMemory() gets called much more frequently than WriteMemory().
  276. Therefore, the more efficient way would be to implement memory mirroring in the
  277. WriteMemory() function.
  278.  
  279.  The ReadMemory()/WriteMemory() functions usually put a lot of overhead on the
  280. emulation, and must be made as efficient as possible, because they get called
  281. very frequently. Here is an example of these functions:
  282. static inline byte ReadMemory(register word Address)
  283. {
  284.   return(MemoryPage[Address>>13][Address&0x1FFF]);
  285. }
  286.  
  287. static inline void WriteMemory(register word Address,register byte Value)
  288. {
  289.   MemoryPage[Address>>13][Address&0x1FFF]=Value;
  290. }
  291. Notice the inline keyword. It will tell compiler to embed the function into the
  292. code, instead of making calls to it. If your compiler does not support inline or
  293. _inline, try making function static: some compilers (WatcomC, for example) will
  294. optimize short static functions by inlining them.
  295.  
  296.  Also, keep in mind that in most cases the ReadMemory() is called several times
  297. more frequently than WriteMemory().Therefore, it is worth to implement most of
  298. the code in WriteMemory(), keeping ReadMemory() as short and simple as possible.
  299.  
  300.  After the opcode is fetched, we decrease the CPU cycle counter by a number of
  301. cycles required for this opcode: Counter-=Cycles[OpCode];The Cycles[] table
  302. should contain the number of CPU cycles for each opcode. Beware that some
  303. opcodes (such as conditional jumps or subroutine calls) may take different
  304. number of cycles depending on their arguments. This can be adjusted later in the
  305. code though.
  306.  
  307.  Now comes the time to interpret the opcode and execute it:
  308. switch(OpCode)
  309. {
  310. It is a common misconception that the switch() construct is inefficient, as it
  311. compiles into a chain of if() ... else if() ... statements. While this is true
  312. for constructs with a small number of cases, the large constructs (100-200 and
  313. more cases) always appear to compile into a jump table, which makes them quite
  314. efficient.
  315.  
  316.  There are two alternative ways to interpret the opcodes. The first is to make a
  317. table of functions and call an appropriate one. This method appears to be less
  318. efficient than a switch(), as you get the overhead from function calls. The
  319. second method would be to make a table of labels, and use the goto statement.
  320. While this method is slightly faster than a switch(), it will only work on
  321. compilers supporting "precomputed labels". Other compilers will not allow you to
  322. create an array of label addresses.
  323.  
  324.  After we successfully interpreted and executed an opcode, the comes a time to
  325. check whether we need any interrupts. At this moment, you can also perform any
  326. tasks which need to be synchronized with the system clock:
  327. if(Counter
  328.  
  329. Following is a short list of things which you may want to do in this if()
  330. statement:
  331.  
  332.  * Check if end of screen is reached and generate VBlank interrupt if so
  333.  * Check if end of scanline is reached and generate HBlank interrupt if so
  334.  * Check for sprite collisions, generate interrupt if necessary
  335.  * Update emulated hardware timers, generate interrupt if timer expires
  336.  * Refresh a display scanline
  337.  * Refresh the entire screen
  338.  * Update sound
  339.  * Read keyboard/joysticks state
  340.  * etc.
  341.  
  342.  Carefully calculate the number of CPU cycles needed for each task, then use the
  343. smallest number for InterruptPeriod, and tie all other tasks to it (they should
  344. not necessarily execute on every expiration of the Counter).
  345.  
  346.  Note that we do not simply assign Counter=InterruptPeriod, but do a
  347. Counter+=InterruptPeriod: this makes cycle counting more precise, as there may
  348. be some negative number of cycles in the Counter.
  349.  
  350.  Also, look at the if(ExitRequired) break;line. As it is too costly to check for
  351. an exit on every pass of the loop, we do it only when the Counter expires: this
  352. will still exit the emulation when you set ExitRequired=1, but it won't take as
  353. much CPU time.
  354.  
  355.  This is about all I have to say about CPU emulation in C. You should be able to
  356. figure the rest on your own.
  357.  
  358. How do I optimize C code?
  359.  
  360.   First, a lot of additional code perfomance can be achieved by choosing right
  361. optimization options for the compiler. Based on my experience, following
  362. combinations of flags will give you the best execution speed:
  363. Watcom C++      -oneatx -zp4 -5r -fp3
  364. GNU C++         -O3 -fomit-frame-pointer
  365. Borland C++
  366.   If you find a better set of options for one of these compilers or a different
  367. compiler, please, let me know about it.
  368.  
  369.  * A little note on loop unrolling:
  370.   It may appear useful to switch on the "loop unrolling" option of the
  371. optimizer. This option will try to convert short loops into linear pieces of
  372. code. My experience shows, though, that this option does not produce any
  373. perfomance boost. Turning it on may also break your code in some very special
  374. cases.
  375.  
  376.   Optimizing the C code itself is slightly trickier than choosing compiler
  377. options, and generally depends on the CPU for which you compile the code.Several
  378. general rules tend to apply to all CPUs. Do not take them for absolute truths
  379. though, as your mileage may vary:
  380.  
  381.  * Size of integers
  382.   Try to use only integers of the base size supported by the CPU, i.e. int ones,
  383. as opposed to short or long. This will reduce amount of code compiler generates
  384. to convert between different integer lengths. It may also reduce the memory
  385. access time, as some CPUs work fastest when reading/writing data of the base
  386. size aligned to the base size address boundaries.
  387.  * Register allocation
  388.   Use as few variables as possible in each block and declare most frequently
  389. used ones as register (most new compilers can automatically put variables into
  390. registers though). This makes more sense for CPUs with many general-purpose
  391. registers (PowerPC) than for ones with a few dedicated registers (Intel 80x86).
  392.  * Unroll small loops
  393.   If you happen to have a small loop which executes a few times, it is always a
  394. good idea to manually unroll it into a linear piece of code. See a note above
  395. about the automatic loop unrolling.
  396.  * Shifts vs. multiplication/division
  397.   Always use shifts wherever you need to multiply or divide by 2^n
  398. (J/128==J>>7). They execute faster on most CPUs. Also, use bitwise AND to
  399. obtain the modulo in such cases (J%128==J&0x7F).
  400.  
  401.    ©997 Copyright by Marat Fayzullin  [fms@freeflight.com]
  402.  
  403.       < Converted by HTMLess v2.4 by Troglobyte/Darkness. Only Amiga... >